package com.shm.leetcode.concurrent;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntConsumer;
/**
 * @author: shm
 * @dateTime: 2020/10/18 16:47
 * @description: 1116. 打印零与奇偶数
 * 假设有这么一个类：
 *
 * class ZeroEvenOdd {
 *   public ZeroEvenOdd(int n) { ... }      // 构造函数
 *   public void zero(printNumber) { ... }  // 仅打印出 0
 *   public void even(printNumber) { ... }  // 仅打印出 偶数
 *   public void odd(printNumber) { ... }   // 仅打印出 奇数
 * }
 * 相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程：
 *
 * 线程 A 将调用 zero()，它只输出 0 。
 * 线程 B 将调用 even()，它只输出偶数。
 * 线程 C 将调用 odd()，它只输出奇数。
 * 每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ，其中序列的长度必须为 2n。
 *
 *
 *
 * 示例 1：
 *
 * 输入：n = 2
 * 输出："0102"
 * 说明：三条线程异步执行，其中一个调用 zero()，另一个线程调用 even()，最后一个线程调用odd()。正确的输出为 "0102"。
 * 示例 2：
 *
 * 输入：n = 5
 * 输出："0102030405"
 */
public class ZeroEvenOdd {
    /**
     * @author: shm
     * @dateTime: 2020/10/18 18:59
     * @description: 解题思路
     * 先前的两道题，我是通过信号量实现的，也考虑过自旋这种模式，但是总超时，后来看到有位朋友在评论谈及可以通过Thread.yeild()释放CPU资源，变茅塞顿开。本题的一种思想其实就是 lock - free
     *
     * 其实本题想表达的意思就是A、B、C三个线程共同的协作，保证:
     *
     * 1.B执行前要A先执行，C执行前要A先执行
     * 2.C要在B执行之前
     * 3.保证输出 n * 2个数(其中有n个0)
     *
     * 说白了就是不管先输出奇数，还是先输出偶数，先输出0。且奇数优先输出于偶数。
     *
     * 那么咱就开始控制这个流程就好了
     *
     * 1.首先执行的是A线程，然后由A线程控制下一个阶段，核心控制
     * 2.由控制器来控制放行的是B还是C。默认
     *
     * private volatile boolean control = true;
     * 保证C先执行。
     * 3.当C先执行完，将控制器“翻面”，然后回退到A线程去处理。
     *
     * 作者：zong-you-yi-tian
     * 链接：https://leetcode-cn.com/problems/print-zero-even-odd/solution/qi-shi-tong-xian-qian-liang-dao-ti-yi-yang-wo-men-/
     */
    private int n;

    public ZeroEvenOdd(int n) {
        this.n = n;
    }
    private AtomicInteger i=new AtomicInteger(0);
//    private AtomicBoolean f=new AtomicBoolean(true);
    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for (int j = 0; j < n; j++) {
            while (i.get()!=0){
                Thread.yield();
            }
            printNumber.accept(0);
//            if (f.get()){
//                i.set(1);
//            }else {
//                i.set(2);
//            }
            if (j%2==0){
                i.set(1);
            }else {
                i.set(2);
            }
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for (int j = 2; j <= n; j+=2) {
            while (i.get()!=2){
                Thread.yield();
            }
            printNumber.accept(j);
//            f.set(true);
            i.set(0);
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for (int j = 1; j <= n; j+=2) {
            while (i.get()!=1){
                Thread.yield();
            }

            printNumber.accept(j);
//            f.set(false);
            i.set(0);
        }
    }
}
